/*+**************************************************************************/
/***                                                                      ***/
/***   This file is distributed under a BSD license.                      ***/
/***   See LICENSE.txt for details.                                       ***/
/***                                                                      ***/
/**************************************************************************+*/

pconly;

asc
{
  cbuffer Wz4MtrlModelPara : register(c28) : slot vs 1
  {
    float4 m[3];
  };
  cbuffer Wz4MtrlSkinPara : register(c32) : slot vs 3
  {
    float4 Skinning[3];
  };
}

/****************************************************************************/

material Wz4MtrlWireframe
{
  vs
  {
    asc vs_3_0
    {
      cbuffer Wz4MtrlWireframePara : register(c0) : slot vs 0
      {
        row_major float4x4  MVP;
        float3 scale; 
      };
      permute Wz4MtrlWireframePerm 
      {
        Inst;
      };

      use Wz4MtrlWireframePara;
      use Wz4MtrlModelPara;
      use Wz4MtrlWireframePerm;
      void main
      (
        in float3 pos : POSITION,
        in float3 normal : NORMAL,
        in float4 col : COLOR0,
        
        in float4 in_mat0 : TEXCOORD5 : pif(Inst),
        in float4 in_mat1 : TEXCOORD6 : pif(Inst),
        in float4 in_mat2 : TEXCOORD7 : pif(Inst),        
        
        out float4 ocol : COLOR0,
        out float4 opos : POSITION,
      )
      {
        float4x3 model;
        pif(Inst)
          model = transpose(float3x4(in_mat0,in_mat1,in_mat2));
        pelse
          model = transpose(float3x4(m[0],m[1],m[2]));

        pos = mul(float4(pos.xyz,1),model);
        opos = mul(float4(pos,1),MVP);
        opos.xyz += normal*scale*opos.w;

        ocol = col;
      }
    }
  }
  ps
  {  
    asc ps_3_0
    {
      float4 main(in float4 col : COLOR0) : COLOR0
      {
        return col;
      }
    }
  }

  prepare
  {
    sInt vs=0;
    sU32 fm = format->GetAvailMask();
    
    if(fm & (1<<sVF_UV5))
      vs |= Wz4MtrlWireframePermMask_Inst;
    VertexShader = VS(vs);
    PixelShader = PS(); 
  }
};

/****************************************************************************/

material Wz4MtrlZOnly
{
  vs asc vs_3_0
  {
    cbuffer Wz4MtrlZOnlyPara : register(c0) : slot vs 0
    {
      row_major float4x4 mvp;
      row_major float4x4 mv;
      float4 proj;
      float4 center;
    };
    permute Wz4MtrlZOnlyPerm 
    {
      Inst;
      Skin;
      ZMode { ZNormal,ZW,ZDist };
      NormalI4;

      assert(NormalI4 implies ZMode == ZNormal);
    };
    permute Wz4MtrlZOnlyPSPerm 
    {
      ZMode { ZNormal,ZW,ZDist };
    };

    use Wz4MtrlZOnlyPara;
    use Wz4MtrlModelPara;
    use Wz4MtrlZOnlyPerm;
    use Wz4MtrlSkinPara;
    void main
    (
      in float3 in_pos : POSITION,
      in float3 in_norm : NORMAL,
      
      in float4 in_mat0 : TEXCOORD5 : pif(Inst),
      in float4 in_mat1 : TEXCOORD6 : pif(Inst),
      in float4 in_mat2 : TEXCOORD7 : pif(Inst),        
      in uint4 in_index : BLENDINDICES : pif(Skin),
      in uint4 in_weighti : BLENDWEIGHT : pif(Skin),      
      out float4 tpos : TEXCOORD0,
      out float3 onorm : TEXCOORD1 : pif(ZMode==ZNormal),
      out float3 tcenter : TEXCOORD1 : pif(ZMode==ZDist),
      out float4 opos : POSITION,
    )
    {
      pif(NormalI4)
        in_norm.xyz = normalize(in_norm.xyz/127.0-1);
      pif(Skin)
      {
        float4 sm0,sm1,sm2,n;

        float4 in_weight = (float4(in_weighti)-127)/127;
        
        sm0  = in_weight.x * Skinning[in_index.x+0];
        sm1  = in_weight.x * Skinning[in_index.x+1];
        sm2  = in_weight.x * Skinning[in_index.x+2];
        sm0 += in_weight.y * Skinning[in_index.y+0];
        sm1 += in_weight.y * Skinning[in_index.y+1];
        sm2 += in_weight.y * Skinning[in_index.y+2];
        sm0 += in_weight.z * Skinning[in_index.z+0];
        sm1 += in_weight.z * Skinning[in_index.z+1];
        sm2 += in_weight.z * Skinning[in_index.z+2];
        sm0 += in_weight.w * Skinning[in_index.w+0];
        sm1 += in_weight.w * Skinning[in_index.w+1];
        sm2 += in_weight.w * Skinning[in_index.w+2];
        
        n = float4(in_pos.xyz ,1);
        in_pos.x  = dot(n,sm0);
        in_pos.y  = dot(n,sm1);
        in_pos.z  = dot(n,sm2);

        pif(ZMode==ZNormal)
        {
          n = float4(in_norm.xyz ,0);
          in_norm.x = dot(n,sm0);
          in_norm.y = dot(n,sm1);
          in_norm.z = dot(n,sm2);          
        }
      }

      float4x3 model;
      pif(Inst)
        model = transpose(float3x4(in_mat0,in_mat1,in_mat2));
      pelse
        model = transpose(float3x4(m[0],m[1],m[2]));

      float3 pos = mul(float4(in_pos.xyz,1),model);
      opos = mul(float4(pos,1),mvp);

      pif(ZMode==ZNormal)
        onorm = mul(float4(in_norm,0),mv).xyz;
      pif(ZMode==ZDist)
      {
        tcenter = center.xyz;
        tpos = pos.xyzz;
      }
      pelse
      {
        tpos = float4(proj.xy,opos.zw);
      }
    } 
  }
  ps asc ps_3_0
  {
    use Wz4MtrlZOnlyPSPerm;
    float4 main(
      in float4 pos : TEXCOORD0, 
      in float3 norm : TEXCOORD1_centroid : pif(ZMode==ZNormal),
      in float3 center : TEXCOORD1 : pif(ZMode==ZDist),
    ) : COLOR0
    {
      // ryg says: why not simply pos.w*(1/ClipFar)? :)
      float depth = pos.z/pos.w;
      float z = pos.y/(depth-pos.x);

      pif(ZMode==ZW)
        return depth;
      pif(ZMode==ZNormal)
        return float4(z,normalize(norm.xyz));
      pif(ZMode==ZDist)
        return length(pos-center);
    }
  }

  header
  {
    sInt SimpleZ;
  }
  new
  {
    SimpleZ = 0;
  }

  prepare
  {
    sInt vs=0;
    sInt ps=0;
    sU32 fm = format->GetAvailMask();
    if(SimpleZ==1)
    {
      vs |= Wz4MtrlZOnlyPerm_ZW;
      ps |= Wz4MtrlZOnlyPSPerm_ZW;
    }
    if(SimpleZ==2)
    {
      vs |= Wz4MtrlZOnlyPerm_ZDist; 
      ps |= Wz4MtrlZOnlyPSPerm_ZDist;
    }

    if(fm & (1<<sVF_UV5))
      vs |= Wz4MtrlZOnlyPermMask_Inst;
    if(fm & (1<<sVF_BONEINDEX))
      vs |= Wz4MtrlZOnlyPermMask_Skin;

    const sU32 *desc = format->GetDesc();
    while(*desc)
    {
      sU32 u = *desc++;
      if((u&sVF_USEMASK)==sVF_NORMAL)
      {
        if((u&sVF_TYPEMASK)==sVF_I4)
          vs |= Wz4MtrlZOnlyPermMask_NormalI4;
      }
    }

    VertexShader = VS(vs);
    PixelShader = PS(ps); 
  }
};

/****************************************************************************/

material Wz4MtrlError
{
  vs asc vs_3_0
  {
    cbuffer Wz4MtrlErrorPara : register(c0) : slot vs 0
    {
      row_major float4x4 mvp;
    };
    permute Wz4MtrlErrorPerm 
    {
      Inst;
      Skin;
    };

    use Wz4MtrlErrorPara;
    use Wz4MtrlModelPara;
    use Wz4MtrlErrorPerm;
    use Wz4MtrlSkinPara;
    void main
    (
      in float3 in_pos : POSITION,
      in float4 in_mat0 : TEXCOORD5 : pif(Inst),
      in float4 in_mat1 : TEXCOORD6 : pif(Inst),
      in float4 in_mat2 : TEXCOORD7 : pif(Inst),        
      in uint4 in_index : BLENDINDICES : pif(Skin),
      in uint4 in_weighti : BLENDWEIGHT : pif(Skin),      

      out float4 opos : POSITION,
    )
    {
      pif(Skin)
      {
        float4 sm0,sm1,sm2,n;

        float4 in_weight = (float4(in_weighti)-127)/127;
        
        sm0  = in_weight.x * Skinning[in_index.x+0];
        sm1  = in_weight.x * Skinning[in_index.x+1];
        sm2  = in_weight.x * Skinning[in_index.x+2];
        sm0 += in_weight.y * Skinning[in_index.y+0];
        sm1 += in_weight.y * Skinning[in_index.y+1];
        sm2 += in_weight.y * Skinning[in_index.y+2];
        sm0 += in_weight.z * Skinning[in_index.z+0];
        sm1 += in_weight.z * Skinning[in_index.z+1];
        sm2 += in_weight.z * Skinning[in_index.z+2];
        sm0 += in_weight.w * Skinning[in_index.w+0];
        sm1 += in_weight.w * Skinning[in_index.w+1];
        sm2 += in_weight.w * Skinning[in_index.w+2];
        
        n = float4(in_pos.xyz ,1);
        in_pos.x  = dot(n,sm0);
        in_pos.y  = dot(n,sm1);
        in_pos.z  = dot(n,sm2);    
      }

      float4x3 model;
      pif(Inst)
        model = transpose(float3x4(in_mat0,in_mat1,in_mat2));
      pelse
        model = transpose(float3x4(m[0],m[1],m[2]));

      float3 pos = mul(float4(in_pos.xyz,1),model);
      opos = mul(float4(pos,1),mvp);
    } 
  }
  ps asc ps_3_0
  {
    float4 main(
    ) : COLOR0
    {
      return float4(1,0,0,1);
    }
  }

  prepare
  {
    sInt vs=0;
    sU32 fm = format->GetAvailMask();

    if(fm & (1<<sVF_UV5))
      vs |= Wz4MtrlErrorPermMask_Inst;
    if(fm & (1<<sVF_BONEINDEX))
      vs |= Wz4MtrlErrorPermMask_Skin;

    VertexShader = VS(vs);
    PixelShader = PS(); 
  }
};

/****************************************************************************/
/****************************************************************************/

material SimpleShader
{
  vs 
  {
    asc vs_3_0
    {
      cbuffer SimpleShaderVEnv : register(c0) : slot vs 0
      {
        row_major float4x4 mvp;   // c0
        row_major float4x4 mv;    // c4
        float4 lc[4];             // c8
        float4 la;                // c12
        float4 ld[3];             // c13
        float4 EyePos;            // c16
      };                          // c17

      cbuffer SimpleShaderVPara : register(c20) : slot vs 2
      {
        float4 texmat[7];         // c20
        float4 vextra;            // c27
      };                          // c28  ... Wz4MtrlModelPara at c28!
      permute SimpleShaderVPerm
      {
        Light;
        Tex { TexOff,Tex1,Tex2 };
        Detail { DetailUV,DetailPos,DetailNorm,DetailRefl };
        MatrixSkin;
        MatrixInst;
        PosOut;
        VTex { VTexOff,VTexUV,VTexPos,VTexNoise };
        NormalI4;

        assert(Detail implies Tex==Tex2);
        assert(NormalI4 implies (Light || Detail==DetailNorm || Detail==DetailRefl || VTex));
      };

      use SimpleShaderVEnv;
      use Wz4MtrlModelPara;
      use SimpleShaderVPara;
      use SimpleShaderVPerm;
      use Wz4MtrlSkinPara;

      sampler2D s0 : register(s0) : pif(VTex);
      
      void main
      (
        in float3 in_pos : POSITION,
        in float3 in_norm : NORMAL : pif(Light || Detail==DetailNorm || Detail==DetailRefl || VTex),
        in float3 in_tang : TANGENT : pif(VTex==VTexUV),
        in float2 in_uv0 : TEXCOORD0 : pif(Tex || VTex==VTexUV),
        
        in uint4 in_index : BLENDINDICES : pif(MatrixSkin),
        in uint4 in_weighti : BLENDWEIGHT : pif(MatrixSkin),
        in float4 in_mat0 : TEXCOORD5 : pif(MatrixInst),
        in float4 in_mat1 : TEXCOORD6 : pif(MatrixInst),
        in float4 in_mat2 : TEXCOORD7 : pif(MatrixInst),
        
        out float4 out_col : COLOR0,
        out float4 out_uv0 : TEXCOORD0 : pif(Tex),
        out float3 out_fog : TEXCOORD1 : pif(PosOut),
        out float4 out_pos : POSITION,
      )
      {
        pif(NormalI4)
          in_norm.xyz = normalize(in_norm.xyz/127.0-1);
        pif(MatrixSkin)
        {
          float4 sm0,sm1,sm2,n;
          
          float4 in_weight = (float4(in_weighti)-127)/127;

          sm0  = in_weight.x * Skinning[in_index.x+0];
          sm1  = in_weight.x * Skinning[in_index.x+1];
          sm2  = in_weight.x * Skinning[in_index.x+2];
          sm0 += in_weight.y * Skinning[in_index.y+0];
          sm1 += in_weight.y * Skinning[in_index.y+1];
          sm2 += in_weight.y * Skinning[in_index.y+2];
          sm0 += in_weight.z * Skinning[in_index.z+0];
          sm1 += in_weight.z * Skinning[in_index.z+1];
          sm2 += in_weight.z * Skinning[in_index.z+2];
          sm0 += in_weight.w * Skinning[in_index.w+0];
          sm1 += in_weight.w * Skinning[in_index.w+1];
          sm2 += in_weight.w * Skinning[in_index.w+2];
          
          n = float4(in_pos.xyz ,1);
          in_pos.x  = dot(n,sm0);
          in_pos.y  = dot(n,sm1);
          in_pos.z  = dot(n,sm2);
          pif(Light)
          {
            n = float4(in_norm.xyz ,0);
            in_norm.x = dot(n,sm0);
            in_norm.y = dot(n,sm1);
            in_norm.z = dot(n,sm2);          
          }
        }

        row_major float4x3 model;
        pif(MatrixInst)
          model = transpose(float3x4(in_mat0,in_mat1,in_mat2));
        pelse
          model = transpose(float3x4(m[0],m[1],m[2]));
        float3 pos = mul(float4(in_pos.xyz,1),model);
        float3 norm = float3(0,0,0);
        pif(Light || Detail==DetailNorm || Detail==DetailRefl || VTex)
          norm = mul(float4(in_norm.xyz,0),model);          

        pif(VTex)
        {
          pif(VTex==VTexNoise)              // special noise mode
          {
            float3 freq,disp;
            
            freq.x = dot(texmat[4],float4(in_pos,1));
            freq.y = dot(texmat[5],float4(in_pos,1));
            freq.z = dot(texmat[6],float4(in_pos,1));

            disp  = tex2Dlod(s0,float4(freq.xy,0,0)).xyz;
            disp += tex2Dlod(s0,float4(freq.xz,0,0)).xyz;
            disp += tex2Dlod(s0,float4(freq.yz,0,0)).xyz;
            
            pos += (disp-1.5) * vextra.x;
          }
          pelse                             // uv or pos
          {
            float4 uv,v;
            pif(VTex==VTexUV)
              v = float4(in_uv0.xy,0,1);
            pelse
              v = float4(in_pos.xyz,1);
            uv.x = dot(texmat[4],v);
            uv.y = dot(texmat[5],v);
            uv.z = 0;
            uv.w = 0;
            float4 t = tex2Dlod(s0,uv)*2-1;
            pos += norm*vextra.x*t.w;
            pif(VTex==VTexUV)
            {
              float3 ts_norm = normalize(norm);
              float3 ts_tang = normalize(in_tang);
              float3 ts_bita = normalize(cross(ts_tang,ts_norm));
              norm = t.x*ts_norm + t.z*ts_tang + t.y*ts_bita;
            }
          }
        }

        pif(Light)
        {
          norm = normalize(norm);
          float4 intensity = norm.x*ld[0] 
                           + norm.y*ld[1] 
                           + norm.z*ld[2];
          intensity = saturate(intensity);
          out_col = lc[0]*intensity.x  
                  + lc[1]*intensity.y 
                  + lc[2]*intensity.z 
                  + lc[3]*intensity.w                
                  + la;
        }
        pelse
        {
          out_col = float4(1,1,1,1);
        }
        pif(Tex)
        {
          float4 uv=float4(in_uv0,0,1);
          out_uv0.x = dot(texmat[0],uv);
          out_uv0.y = dot(texmat[1],uv);
          out_uv0.zw = 0;
        }
        pif(Tex==Tex2)      // DetailUV,DetailPos,DetailNorm,DetailRefl
        {
          float4 uv;
          pif(Detail==DetailUV)   uv=float4(in_uv0,0,1);
          pif(Detail==DetailPos)  uv=float4(in_pos,1);
          pif(Detail==DetailNorm) uv=float4(normalize(norm),1);
          pif(Detail==DetailRefl) uv=float4(reflect(normalize(pos.xyz-EyePos.xyz),normalize(norm)),1);
           
          out_uv0.z = dot(texmat[2],uv);
          out_uv0.w = dot(texmat[3],uv);
        }
        out_pos = mul(float4(pos,1),mvp);
        pif(PosOut)
          out_fog = mul(float4(pos,1),mv).xyz;
      }
    }
  }
  
  ps
  {
    asc ps_3_0
    {
      cbuffer SimpleShaderPEnv : register(c0) : slot ps 0
      {
        float4 FogPara;
        float4 FogColor;
        float4 ClipPlane[4];
      };
      permute SimpleShaderPPerm
      {
        Tex1;
        Tex2 { Tex2Off,Tex2Add,Tex2Mul };
        Fog { FogOff,FogOn,FogBlack,FogClip3 };
        Clip;
        assert(Tex2 implies Tex1);
      };
      
      use SimpleShaderPPerm;
      use SimpleShaderPEnv;

      sampler2D s0 : register(s0) : pif(Tex1);
      sampler2D s1 : register(s1) : pif(Tex2);

      void main
      (
        in float4 in_col : COLOR0,
        in float4 in_uv0 : TEXCOORD0 : pif(Tex1),
        in float3 in_pos : TEXCOORD1 : pif(Fog!=FogOff || Clip),
        out float4 out_col : COLOR0,
      )
      {
        pif(Clip)
        {
          float4 clipdot;
          clipdot.x = dot(float4(in_pos,1),ClipPlane[0]);
          clipdot.y = dot(float4(in_pos,1),ClipPlane[1]);
          clipdot.z = dot(float4(in_pos,1),ClipPlane[2]);
          clipdot.w = dot(float4(in_pos,1),ClipPlane[3]);
          clip(clipdot);
        }

        out_col = in_col;
        pif(Tex1)
          out_col *= tex2D(s0,in_uv0.xy);
        pif(Tex2==Tex2Add)
          out_col += tex2D(s1,in_uv0.zw);
        pif(Tex2==Tex2Mul)
          out_col *= tex2D(s1,in_uv0.zw);
        pif(Fog!=FogOff)
        {
          float fog = length(in_pos);
          if(Fog==FogClip3)
          {
            float d = dot(normalize(in_pos),ClipPlane[3].xyz);
            if(d < 0)
              fog = min(fog,-dot(float4(in_pos,1),ClipPlane[3])/-d);
            else 
              fog = min(fog,ClipPlane[3].w/-d);
          }
          fog = saturate((fog+FogPara.x)*FogPara.y);
          pif(Fog==FogClip3)
            fog = 3*fog*fog-2*fog*fog*fog;
          pelse
            fog = 2*fog-fog*fog;
          fog = fog*FogColor.w;
          if(Fog==FogBlack)
            out_col.xyz *= 1-fog;
          else
            out_col.xyz = lerp(out_col.xyz,FogColor.xyz,fog);
        }
      }
    }
  }
  
  header
  {
    enum ExtraBits
    {
      Extra_Fog           = 0x0001,
      Extra_FogBlack      = 0x0002,
      Extra_TexMul        = 0x0004,
      Extra_Clip          = 0x0008,
      Extra_NormalI4      = 0x1000,

      ExtraDetailMask     = 0x00f0,
      ExtraDetailUV       = 0x0000,
      ExtraDetailPos      = 0x0010,
      ExtraDetailNorm     = 0x0020,
      ExtraDetailRefl     = 0x0030,

      ExtraVTexMask       = 0x0300,
      ExtraVTexUV         = 0x0000,
      ExtraVTexPos        = 0x0100,
      ExtraVTexNoise      = 0x0200,
    };
    sInt Extra;
  }
  new
  {
    Extra = 0;
  }
  
  
  prepare
  {
    sInt ps=0,vs=0;
    sU32 fm = format->GetAvailMask();
    
    if(Texture[0])
    {
      ps |= SimpleShaderPPermMask_Tex1;
      if(!Texture[1])
        vs |= SimpleShaderVPerm_Tex1;
    }
    if(Texture[1])
    {
      if(Extra & Extra_TexMul)
        ps |= SimpleShaderPPerm_Tex2Mul;
      else
        ps |= SimpleShaderPPerm_Tex2Add;
      vs |= SimpleShaderVPerm_Tex2;
      switch(Extra & ExtraDetailMask)
      {
        default:
        case ExtraDetailUV:   vs |= SimpleShaderVPerm_DetailUV;   break;
        case ExtraDetailPos:  vs |= SimpleShaderVPerm_DetailPos;  break;
        case ExtraDetailNorm: vs |= SimpleShaderVPerm_DetailNorm; break;
        case ExtraDetailRefl: vs |= SimpleShaderVPerm_DetailRefl; break;
      }
    }
    if(Texture[2])
    {
      switch(Extra & ExtraVTexMask)
      {
        case ExtraVTexUV:    vs |= SimpleShaderVPerm_VTexUV; break;
        case ExtraVTexPos:   vs |= SimpleShaderVPerm_VTexPos; break;
        case ExtraVTexNoise: vs |= SimpleShaderVPerm_VTexNoise; break;
        default: break;
      }
    }
    if((Extra&3)==1)
    {
      ps |= SimpleShaderPPerm_FogOn;
      vs |= SimpleShaderVPermMask_PosOut;
    }
    else if((Extra&3)==2)
    {
      ps |= SimpleShaderPPerm_FogBlack;
      vs |= SimpleShaderVPermMask_PosOut;
    }
    else if((Extra&3)==3)
    {
      ps |= SimpleShaderPPerm_FogClip3;
      vs |= SimpleShaderVPermMask_PosOut;
    }
    if(Extra&8)
    {
      ps |= SimpleShaderPPermMask_Clip;
      vs |= SimpleShaderVPermMask_PosOut;
    }

    if((fm & (1<<sVF_NORMAL)) && (Flags & sMTRL_LIGHTING))
      vs |= SimpleShaderVPermMask_Light;
    if(fm & (1<<sVF_UV7))
      vs |= SimpleShaderVPermMask_MatrixInst;
    if(fm & (1<<sVF_BONEINDEX))
      vs |= SimpleShaderVPermMask_MatrixSkin;

    const sU32 *desc = format->GetDesc();
    while(*desc)
    {
      sU32 u = *desc++;
      if((u&sVF_USEMASK)==sVF_NORMAL)
      {
        if((u&sVF_TYPEMASK)==sVF_I4)
          vs |= SimpleShaderVPermMask_NormalI4;
      }
    }

    VertexShader = VS(vs);
    PixelShader = PS(ps); 
  }
};

/****************************************************************************/

asc
{

  cbuffer CustomShaderVEnv : register(c0) : slot vs 0
  {
    row_major float4x4 mvp;   // c0
    row_major float4x4 mv;    // c4
    float4 EyePos;            // c8
    float4 vs_var0;           // c9
    float4 vs_var1;           // c10
    float4 vs_var2;           // c11
    float4 vs_var3;           // c12
    float4 vs_var4;           // c13
  };                   
  
  cbuffer CustomShaderPEnv : register(c0) : slot ps 0
  {
    row_major float4x4 mvp;   // c0
    row_major float4x4 mv;    // c4
    float4 EyePos;            // c8
    float4 ps_var0;           // c9
    float4 ps_var1;           // c10
    float4 ps_var2;           // c11
    float4 ps_var3;           // c12
    float4 ps_var4;           // c13
  };

}